" Searching word with sdcv

augroup Sdcv_plugin_keymap
  autocmd!
  autocmd filetype sdcv nnoremap <buffer><silent> q :close!<cr>
  autocmd filetype sdcv nnoremap <buffer><silent> <c-p> :call search("-->.*\\n-->", "bw") \| normal zt<cr>
  autocmd filetype sdcv nnoremap <buffer><silent> <c-n> :call search("-->.*\\n-->", "w") \| normal zt<cr>
augroup END

if !exists("g:sdcv_dictionary_simple_list")
  let g:sdcv_dictionary_simple_list = []
end

if !exists("g:sdcv_dictionary_complete_list")
  let g:sdcv_dictionary_complete_list = []
end

function! s:sdcv_search_with_dictionary(word, dict_list)
  if len(a:dict_list) > 0 && has("unix")
    let dict_args = "-u " . join(a:dict_list, " -u ")
  else
    let dict_args = ""
  endif

  let sdcv_cmd = 'sdcv --utf8-output --utf8-input ' . dict_args .  ' -n "' . a:word . '"'
  let result = system(sdcv_cmd)

  return result
endfunction

function! s:sdcv_formart_result(result) abort
  let result = substitute(a:result, "[\\.] \\*", "\n*", "g")
  let result = substitute(a:result, " \\(\\d [([]\\)", "\n\\1", "g")
  let result = substitute(a:result, "\\(\\d [([]\\)", "\n\\1", "g")
  let result = substitute(a:result, " \\((\\w)\\)", "\n\\1", "g")
  return result
endfunction

function! s:sdcv_nvim_show_detail_result(text)
  let default_height = &lines - 3
  let text_height = count(a:text, "\n")
  let height = default_height / 2 > text_height ? text_height : default_height / 2
  let width = float2nr(&columns - (&columns * 2 / 10))
  let col = float2nr((&columns - width) / 2)

  let opts = {
        \'relative': 'editor',
        \'row': default_height * 0.3,
        \'col': col + 30,
        \'width': width * 3 / 4,
        \'height': height,
        \'focusable': 1
        \}

  let buf = nvim_create_buf(v:false, v:true)
  let winid = nvim_open_win(buf, v:true, opts)

  " optional: change highlight, otherwise Pmenu is used
  call nvim_win_set_option(winid, 'winhl', 'Normal:Pmenu')

  setlocal
        \ buftype=nofile
        \ filetype=sdcv
        \ bufhidden=wipe
        \ nobuflisted
        \ nonumber
        \ nohlsearch
        \ nowrap
        \ norelativenumber
        \ signcolumn=no
        \ noswapfile
        \ undolevels=-1

  call nvim_buf_set_lines(buf, 0, -1, v:true, split(a:text, '\n'))
endfunction

function! s:sdcv_nvim_show_result(text,type)
  if a:type == "simple"
    echo a:text
  else
    call s:sdcv_nvim_show_detail_result(a:text)
  end
endfunction

function! s:sdcv_show_result(text,type)
  if exists('*nvim_open_win')
    call s:sdcv_nvim_show_result(a:text,a:type)
  else
    echo a:text
  end
endfunction

function! s:sdcv_pick_word()

  let text = s:sdcv_replace_nonword_character(expand("<cword>"))

  let pos1 = getpos(".")
  execute "normal b"
  let pos2 = getpos(".")
  call setpos(".", add(pos1, pos1[2]))

  let words = split(text, " ")

  let offset = pos1[2] - pos2[2]
  if pos2[1] < pos1[1]
    let offset = 0
  endif

  let total = eval(join(map(copy(words), {_, v->strlen(v)}), "+"))
  if offset > total
    return words[-1]
  end

  let search_index = 0
  for word in words
    if offset >=  search_index &&  offset < search_index + strlen(word)
      return word
    end

    let search_index = search_index + strlen(word)
  endfor
  return words[-1]
endfunction

function! s:sdcv_replace_nonword_character(word)
  " set ignorecase will change search action.
  " so we need add \c to the pattern.
  let result = substitute(a:word, '\C\([a-z0-9]\)\([A-Z]\)'   , '\1_\2', "g")
  let result = substitute(result, '\C\([A-Z]+\)\([A-Z][a-z]\)', '\1_\2', "g")
  let result = substitute(result, '\.'                        , '_'    , "g")
  let result = substitute(result, '-'                         , '_'    , "g")
  let result = substitute(result, '#'                         , '_'    , "g")
  let result = substitute(result, '_+'                        , '_'    , "g")
  let result = substitute(result, '_'                         , ' '    , "g")
  return result
endfunction

function! s:sdcv_get_visual_selection()
  let [line_start, column_start] = getpos("'<")[1:2]
  let [line_end, column_end] = getpos("'>")[1:2]
  let lines = getline(line_start, line_end)
  if len(lines) == 0
    return ''
  endif
  let lines[-1] = lines[-1][: column_end - 1]
  let lines[0] = lines[0][column_start - 1:]
  return join(lines, "\n")
endfunction

function! sdcv#search_input(bang, word) abort
  let search_result = s:sdcv_search_with_dictionary(a:word , g:sdcv_dictionary_complete_list)
  if a:bang
    call s:sdcv_show_result(s:sdcv_formart_result(search_result), "detail")
  else
    call s:sdcv_show_result(search_result, "simple")
  endif
endfunction

function! sdcv#search_selection()
  let word = s:sdcv_get_visual_selection()
  let word = s:sdcv_replace_nonword_character(word)
  let search_result = s:sdcv_search_with_dictionary(word , g:sdcv_dictionary_simple_list)
  call s:sdcv_show_result(search_result, "simple")
endfunction

function! sdcv#search_detail_selection()
  let word = s:sdcv_get_visual_selection()
  let word = s:sdcv_replace_nonword_character(word)
  let search_result = s:sdcv_search_with_dictionary(word , g:sdcv_dictionary_complete_list)
  call s:sdcv_show_result(s:sdcv_formart_result(search_result), "detail")
endfunction

function! sdcv#search_pointer(mode)
  if a:mode == "v"
    call sdcv#search_selection()
    return
  endif
  let word = s:sdcv_pick_word()
  let search_result = s:sdcv_search_with_dictionary(word , g:sdcv_dictionary_simple_list)
  call s:sdcv_show_result(search_result, "simple")
endfunction

function! sdcv#search_detail_pointer(mode)
  if a:mode == "v"
    call sdcv#search_detail_selection()
    return
  endif
  let word = s:sdcv_pick_word()
  let search_result = s:sdcv_search_with_dictionary(word , g:sdcv_dictionary_complete_list)
  call s:sdcv_show_result(s:sdcv_formart_result(search_result), "detail")
endfunction

command! -nargs=1 -bang Sdcv :call sdcv#search_input(<bang>0, <q-args>)
nnoremap <silent> <C-w>r  :<c-u>call sdcv#search_pointer("n")<cr>
vnoremap <silent> <C-w>r v:<c-u>call sdcv#search_pointer("v")<cr>
nnoremap <silent> <C-w>e  :<c-u>call sdcv#search_detail_pointer("n")<cr>
vnoremap <silent> <C-w>e v:<c-u>call sdcv#search_detail_pointer("v")<cr>
